{

Parallax Flick-It Prize Picker + checker
Version 1.0
November 1st, 2010

This program has been slightly modified to test proper random distribution of a random number generator.
It works by picking a random number, then taking the modulous of the total number of contestants.  This
yields an index from 0 -> # of contestants.  This is how a winners name is chosen.  The winning index
is plugged into a long array meant to count how many times that particular index number has been chosen.
The program "picks" a random winner after about every 1/4 second, so 4 winners per second.

True randomization exists if the winning counts of this test only vary minimally between winner picks.
If you see that one particular array index has a significantly higher count than all the others, then
the random number generator is not really random.

Ideally, when the simulation is over, the value of each index should be approximately the following:

(index count) / (total number of picks)


Here are the results of one of my simulations:

           Distribution
Total numbers picked: 921690
name[1]=29569
name[2]=29818
name[3]=29892
name[4]=29818
name[5]=29550
name[6]=29836
name[7]=29979
name[8]=29671
name[9]=29750
name[10]=29586
name[11]=29921
name[12]=29480
name[13]=29643
name[14]=29336
name[15]=29539
name[16]=29723
name[17]=29900
name[18]=29525
name[19]=29698
name[20]=30204
name[21]=29692
name[22]=29708
name[23]=29787
name[24]=29883
name[25]=29630
name[26]=29860
name[27]=29873
name[28]=29791
name[29]=29610
name[30]=29958
name[31]=29460


Program written by: Daniel Harris

This program is free of copyright.  Feel free to take this code and use it however you want.




}
con


  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

  VibraTabPin   = 0
  again_pin     = 1

  

var

  byte num_contestants
  byte flicked

  long already[256]
  
  long flick_stack[20]


obj

  pst:  "Parallax Serial Terminal"
  rr:   "RealRandom"

dat

long
long
              '         10        20        30 31
              '0123456789012345678901234567890  1
name1   byte  "Ryan Clark                     ",0
name2   byte  "Rico Oqueado                   ",0
name3   byte  "Angel Flores                   ",0
name4   byte  "Mark Lerude                    ",0
name5   byte  "Alan Rooke                     ",0
name6   byte  "Clinton Rowley                 ",0
name7   byte  "Mark Headrick                  ",0
name8   byte  "Lyle Hintz                     ",0
name9   byte  "Todd VanBockem                 ",0
name10  byte  "Vladimis Gorbatyuk             ",0
name11  byte  "Raymond Kelly                  ",0
name12  byte  "Konstantin Kondratyev          ",0
name13  byte  "Roman Kravehenrio              ",0
name14  byte  "Justin Weber                   ",0
name15  byte  "Yvonne Marshall                ",0
name16  byte  "Jason Wagnon                   ",0
name17  byte  "Giovanni Escutia               ",0
name18  byte  "Francisco Ramirez              ",0
name19  byte  "Ryan O'Dell                    ",0
name20  byte  "Ian Dietler                    ",0
name21  byte  "Bob Olson                      ",0
name22  byte  "Gregg Erickson                 ",0
name23  byte  "Tim Ballas                     ",0
name24  byte  "Michael Steed                  ",0
name25  byte  "Drew Walton                    ",0
name26  byte  "Tony Dominguez                 ",0
name27  byte  "Ryan Boeck                     ",0
name28  byte  "Richard F. Smith               ",0
name29  byte  "Kevin Woods                    ",0
name30  byte  "Ryan Eloven                    ",0
name31  byte  "Gary George                    ",0
name32  byte  "                               ",0
name33  byte  "                               ",0
name34  byte  "                               ",0
name35  byte  "                               ",0
name36  byte  "                               ",0
name37  byte  "                               ",0
name38  byte  "                               ",0
name39  byte  "                               ",0
name40  byte  "                               ",0
name41  byte  "                               ",0



pub go | i, picks

  cognew(flick_me, @flick_stack)   'start the flick generator

  pst.start(115_200)         'start the serial terminal
  

  rr.start                   'start the random number engine

  dira[again_pin]~


  waitcnt(cnt+clkfreq<<1)    'wait 2 seconds


  
  repeat i from 0 to 255     'for good measure, clear the counting array
    already[i] := false

  num_contestants~           'for good measure, clear the number of contestants
  
  'count the number of names entered.  looks for things that dont start with a " " (space).
  repeat until byte[@name1 + num_contestants*32] ==  " "
    num_contestants++

  picks~                     'for good measure, clear the total number of picks we have done
  
  repeat while ina[again_pin]   'simulate many picks and add up how many times each person gets picked
                                'this will loop while again_pin is high, so pull the pin low to jump out of the loop and print the statistics.
    pst.clear
    pst.str(STRING("=====  Parallax Flick-It Prize Picker  =====", 13, 13))
     
    pst.str(STRING("Today, we have "))
    pst.dec(num_contestants)
    pst.str(STRING(" contestants participating.", 13, 13))
     
    pst.str(STRING("           Whose it gonna be??!!?!?", 13))
     
    flicked~
     
    i~
     

    repeat until flicked        'generate random number until a simulate flick event is caught
     
      pst.Position(0, 7)
      i := rr.random & $7FFF_FFFF    ' get a new random number and chop the sign bit off
      print_name(i//num_contestants)
      pause(50_000)                  'wait a bit of time for the data to come out on the serial terminal
      

    pst.Position(0, 7)

    i := (rr.random & $7FFF_FFFF)    'one last random number, sans 'ole signey bit.  The winner is determined here.

    already[i//num_contestants]++    'find the "pick" spot in the array and increment it,
    picks++                          'increment the total number of picks
    
    print_name(i//num_contestants)   'print the simulated winner
    pause(100_000)                   'wait for 1/10th of a second

  'once the again pin is driven low, we get here

  'print out the distribution of our random test  
  pst.clear
  pst.str(STRING("           Distribution", 13))
  pst.str(string("Total numbers picked: "))
  pst.dec(picks)
  pst.char(13)
     
  repeat i from 0 to num_contestants - 1
    pst.str(STRING("name["))
    pst.dec(i+1)
    pst.str(string("]="))
    pst.dec(already[i])
    pst.char(13)

  repeat  'all done, wait here forever (or until the reset button is pressed)
      
    


pub print_name(name_idx) | j, old

  'a fancy way to automatically center the name on the screen.
  'takes into account the number of characters in the name.

  j := 0
  repeat until byte[@name1 + (name_idx * 32) + j] == " " and old == " "
    old := byte[@name1 + (name_idx * 32) + j]
    j++

  j := (45 - j)>>1

  repeat j
    pst.str(STRING(" "))        'print the correct number of spaces to get the name centered

  pst.str(@name1 + (name_idx*32))  'print the name


pub flick_me                    'runs in its own cog.  Continuously generates a "flicked" event.
                                'This will genearte a flicked event every 1/4 second.
                                
  dira[VibraTabPin]~            'make it an input

  flicked~
  repeat                        'do it forever
    flicked~~                   'generate the flick
    waitcnt(cnt + clkfreq>>2)   'wait 1/4 second
    flicked~                    'clear the flick
  
    
pri Pause(duration)

  'This function pauses program execution for approximately [duration] micro seconds
  'so 1_000_000 would be approximately one second.  Doesnt account for instruction
  'execution time overhead.

  if duration < 381
    duration := 381             'lower bound limit. anything lower than this, we
                                'have to wait until the counter comes back around,
                                'which is MUCH longer than [duration].
  waitcnt(((clkfreq / 1_000_000) * duration) + cnt)    
  